﻿using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using ShevaEngine.Core.GameLogic;
using ShevaEngine.Core.Modules.Graphics;
using ShevaEngine.Core.Modules.Graphics.Cameras;
using Microsoft.Xna.Framework.Content;

namespace ShevaEngine.Core.Modules.Scenes
{
    /// <summary>
    /// Scene object class.
    /// </summary>
    public class SceneObject
    {        
        /// <summary>Local world matrix.</summary>
        [XmlIgnore, ContentSerializerIgnore]
        private Matrix _World;
        /// <summary>World matrix.</summary>
        public virtual Matrix World
        {
            get { return this._World; }
            set
            {
                this._World = value;

                foreach (SceneObject sceneObject in this.AttachedSceneObjects)
                    sceneObject.Transform(this._World);
            }
        }
        /// <summary>Origin matrix.</summary>
        private Matrix OriginMatrix;        
        /// <summary>Origin.</summary>
        [XmlIgnore, ContentSerializerIgnore]
        public Vector3 OriginTranslation { get { return this.OriginMatrix.Translation; } set { this.OriginMatrix.Translation = value; } }
        /// <summary>Scene object.</summary>
        [XmlIgnore, ContentSerializerIgnore]
        public List<SceneObject> AttachedSceneObjects;
        /// <summary>Bounding box.</summary>
        public BoundingBox BoundingBox;
        /// <summary>Flags.</summary>
        private SceneObjectFlags Flags;

        /// <summary>Visible.</summary>
        [XmlIgnore, ContentSerializerIgnore]
        public bool Visible
        {
            get { return (this.Flags & SceneObjectFlags.Visible) == SceneObjectFlags.Visible; }
            set
            {
                if (this.Visible != value)
                    if (value)
                        this.Flags = this.Flags | SceneObjectFlags.Visible;
                    else
                        this.Flags = this.Flags & ~SceneObjectFlags.Visible;
            }
        }

        /// <summary>Enabled.</summary>
        [XmlIgnore, ContentSerializerIgnore]
        public bool Enabled
        {
            get { return (this.Flags & SceneObjectFlags.Enabled) == SceneObjectFlags.Enabled; }
            set
            {
                if (this.Enabled != value)
                    if (value)
                        this.Flags = this.Flags | SceneObjectFlags.Enabled;
                    else
                        this.Flags = this.Flags & ~SceneObjectFlags.Enabled;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public SceneObject()
        {
            this.OriginMatrix = Matrix.Identity;
            this.AttachedSceneObjects = new List<SceneObject>();
            this.World = Matrix.Identity;
            this.Flags = SceneObjectFlags.Enabled | SceneObjectFlags.Visible;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="transformation"></param>
        public SceneObject(Matrix transformation)
        {
            this.OriginMatrix = Matrix.Identity;
            this.AttachedSceneObjects = new List<SceneObject>();
            this.World = transformation;
            this.Flags = SceneObjectFlags.Enabled | SceneObjectFlags.Visible;
        }      

        /// <summary>
        /// Methods add items in rendering pipeline.
        /// </summary>
        /// <param name="renderinPipeline"></param>
        public virtual void AddInRenderingPipeline(RenderingPipeline renderingPipeline, Camera camera) { }

        /// <summary>
        /// Methods add items in rendering pipeline.
        /// </summary>
        /// <param name="renderinPipeline"></param>
        public virtual void AddInRenderingPipeline(RenderingPipeline renderingPipeline, Vector3 position, float distance) { }

        /// <summary>
        /// Method attach to scene object.
        /// </summary>
        /// <param name="sceneObject"></param>
        public virtual void AttachToSceneObject(SceneObject sceneObject, Matrix origin)
        {
            this.AttachedSceneObjects.Add(sceneObject);
            sceneObject.OriginMatrix = origin;

            sceneObject.Transform(this.World);
        }

        /// <summary>
        /// Method attach to scene object.
        /// </summary>
        /// <param name="sceneObject"></param>
        public virtual void AttachToSceneObject(SceneObject sceneObject, Vector3 origin)
        {
            this.AttachedSceneObjects.Add(sceneObject);
            sceneObject.OriginTranslation = origin;

            sceneObject.Transform(this.World);
        }

        /// <summary>
        /// Method transforms scene object world matrix.
        /// </summary>
        /// <param name="matrix"></param>
        public virtual void Transform(Matrix matrix)
        {
            this.World = this.OriginMatrix * matrix;
        }
    }

    /// <summary>
    /// Scene object flags.
    /// </summary>
    public enum SceneObjectFlags : int
    {
        None = 0x0,
        Visible = 0x1,
        Enabled = 0x2,
    }
}
